/**
 * AcFun Format Parser
 * Takes in JSON and parses it based on current documentation for AcFun comments
 * @license MIT License
 **/
var AcfunFormat = (function () {
  var AcfunFormat = {};

  AcfunFormat.JSONParser = function (params) {
    this._logBadComments = true;
    this._logNotImplemented = false;
    if (typeof params === 'object') {
      this._logBadComments = params.logBadComments === false ? false : true;
      this._logNotImplemented = params.logNotImplemented === true ? true : false;
    }
  };

  AcfunFormat.JSONParser.prototype.parseOne = function (comment) {
    // Read a comment and generate a correct comment object
    var data = {};
    if (typeof comment !== 'object' || comment == null || !comment.hasOwnProperty('c')) {
      // This cannot be parsed. The comment contains no config data
      return null;
    }
    var config = comment['c'].split(',');
    if (config.length >= 6) {
      data.stime = parseFloat(config[0]) * 1000;
      data.color = parseInt(config[1])
      data.mode = parseInt(config[2]);
      data.size = parseInt(config[3]);
      data.hash = config[4];
      data.date = parseInt(config[5]);
      data.position = "absolute";
      if (data.mode !== 7) {
        // Do some text normalization on low complexity comments
        data.text = comment.m.replace(/(\/n|\\n|\n|\r\n|\\r)/g,"\n");
        data.text = data.text.replace(/\r/g,"\n");
        data.text = data.text.replace(/\s/g,"\u00a0");
      } else {
        try {
          var x = JSON.parse(comment.m);
        } catch (e) {
          if (this._logBadComments) {
            console.warn('Error parsing internal data for comment');
            console.log('[Dbg] ' + data.text);
          }
          return null; // Can't actually parse this!
        }
        data.position = "relative";
        data.text = x.n; /*.replace(/\r/g,"\n");*/
        data.text = data.text.replace(/\ /g,"\u00a0");
        if (typeof x.a === 'number') {
          data.opacity = x.a;
        } else {
          data.opacity = 1;
        }
        if (typeof x.p === 'object') {
          // Relative position
          data.x = x.p.x / 1000;
          data.y = x.p.y / 1000;
        } else {
          data.x = 0;
          data.y = 0;
        }
        if (typeof x.c === 'number') {
          switch (x.c) {
            case 0: data.align = 0; break;
            case 2: data.align = 1; break;
            case 6: data.align = 2; break;
            case 8: data.align = 3; break;
            default:
              if (this._logNotImplemented) {
                console.log('Cannot handle aligning to center! AlignMode=' + x.c);
              }
          }
        }
        // Use default axis
        data.axis = 0;
        data.shadow = x.b;
        data.dur = 4000;
        if (typeof x.l === 'number') {
          data.dur = x.l * 1000;
        }
        if (x.z != null && x.z.length > 0) {
          data.movable = true;
          data.motion = [];
          var moveDuration = 0;
          var last = {
            x: data.x,
            y: data.y,
            alpha: data.opacity,
            color: data.color
          };
          for (var m = 0; m < x.z.length; m++) {
            var dur = x.z[m].l != null ? (x.z[m].l * 1000) : 500;
            moveDuration += dur;
            var motion = {};
            if (x.z[m].hasOwnProperty('rx') && typeof x.z[m].rx === 'number') {
              // TODO: Support this
              if (this._logNotImplemented) {
                console.log('Encountered animated x-axis rotation. Ignoring.');
              }
            }
            if (x.z[m].hasOwnProperty('e') && typeof x.z[m].e === 'number') {
              // TODO: Support this
              if (this._logNotImplemented) {
                console.log('Encountered animated y-axis rotation. Ignoring.');
              }
            }
            if (x.z[m].hasOwnProperty('d') && typeof x.z[m].d === 'number') {
              // TODO: Support this
              if (this._logNotImplemented) {
                console.log('Encountered animated z-axis rotation. Ignoring.');
              }
            }
            if (x.z[m].hasOwnProperty('x') && typeof x.z[m].x === 'number') {
              motion.x = {
                from: last.x,
                to: x.z[m].x / 1000,
                dur: dur,
                delay: 0
              };
            }
            if (x.z[m].hasOwnProperty('y') && typeof x.z[m].y === 'number') {
              motion.y = {
                from: last.y,
                to: x.z[m].y / 1000,
                dur: dur,
                delay: 0
              };
            }
            last.x = motion.hasOwnProperty('x') ? motion.x.to : last.x;
            last.y = motion.hasOwnProperty('y') ? motion.y.to : last.y;
            if (x.z[m].hasOwnProperty('t') &&
              typeof x.z[m].t === 'number' &&
              x.z[m].t !== last.alpha) {
              motion.alpha = {
                from: last.alpha,
                to: x.z[m].t,
                dur: dur,
                delay: 0
              };
              last.alpha = motion.alpha.to;
            }
            if (x.z[m].hasOwnProperty('c') &&
              typeof x.z[m].c === 'number' &&
              x.z[m].c !== last.color) {
              motion.color = {
                from: last.color,
                to:x.z[m].c,
                dur: dur,
                delay: 0
              };
              last.color = motion.color.to;
            }
            data.motion.push(motion);
          }
          data.dur = moveDuration + (data.moveDelay ? data.moveDelay : 0);
        }
        if (x.hasOwnProperty('w')) {
          if (x.w.hasOwnProperty('f')) {
            data.font = x.w.f;
          }
          if (x.w.hasOwnProperty('l') && Array.isArray(x.w.l)) {
            if (x.w.l.length > 0) {
              // Filters
              if (this._logNotImplemented) {
                console.log('[Dbg] Filters not supported! ' +
                  JSON.stringify(x.w.l));
              }
            }
          }
        }
        if (x.r != null && x.k != null) {
          data.rX = x.r;
          data.rY = x.k;
        }

      }
      return data;
    } else {
      // Not enough arguments.
      if (this._logBadComments) {
        console.warn('Dropping this comment due to insufficient parameters. Got: ' + config.length);
        console.log('[Dbg] ' + comment['c']);
      }
      return null;
    }
  };

  AcfunFormat.JSONParser.prototype.parseMany = function (comments) {
    if (!Array.isArray(comments)) {
      return null;
    }
    var list = [];
    for (var i = 0; i < comments.length; i++) {
      var comment = this.parseOne(comments[i]);
      if (comment !== null) {
        list.push(comment);
      }
    }
    return list;
  };

  AcfunFormat.TextParser = function (param) {
    this._jsonParser = new AcfunFormat.JSONParser(param);
  }

  AcfunFormat.TextParser.prototype.parseOne = function (comment) {
    try {
      return this._jsonParser.parseOne(JSON.parse(comment));
    } catch (e) {
      console.warn(e);
      return null;
    }
  }

  AcfunFormat.TextParser.prototype.parseMany = function (comment) {
    try {
      return this._jsonParser.parseMany(JSON.parse(comment));
    } catch (e) {
      console.warn(e);
      return null;
    }
  }

  return AcfunFormat;
})();
